package kz.kstu.scantest.business.client.reader.com;

import kz.kstu.scantest.business.client.reader.DataListener;
import kz.kstu.scantest.business.util.StringUtil;
import org.apache.log4j.Logger;

import javax.comm.*;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.TooManyListenersException;


/**
 * @author Filipp_Stankevich
 *         created: 16.09.11 21:28
 */
public class COMPort {

    private final static Logger LOGGER = Logger.getLogger(COMPort.class);

    private static final int OPEN_TIMEOUT = 2000;
    private static final int PORT_SPEED = 9600;
    private static final String PORT_NAME = "Scantest";

    private String portName;
    private InputStream inputStream;
    private OutputStream outputStream;
    private SerialPort serialPort;
    private List<DataListener> listeners = new ArrayList<DataListener>();

    public COMPort(String port) {
        this.portName = port;
    }

    public void initialize() throws IOException, TooManyListenersException, UnsupportedCommOperationException,
            PortInUseException, NoSuchPortException {

        CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(portName);

        LOGGER.debug("Port was found = " + portId.getName());

        serialPort = (SerialPort) portId.open(PORT_NAME, OPEN_TIMEOUT);

        LOGGER.debug("Port was open = " + portId.getName());

        serialPort.setSerialPortParams(PORT_SPEED, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
        serialPort.notifyOnDataAvailable(true);
        serialPort.addEventListener(new SerialEventListener());

        outputStream = serialPort.getOutputStream();
        inputStream = serialPort.getInputStream();
    }

    public void write(byte[] data) throws IOException {
        outputStream.write(data);
    }

    public void shutdown() {
        serialPort.close();
        LOGGER.debug("Port was closed.");
    }

    public void addDataListener(DataListener changeListener) {
        listeners.add(changeListener);
    }

    private class SerialEventListener implements SerialPortEventListener {

        @Override
        public void serialEvent(SerialPortEvent serialPortEvent) {
            try {
                if (serialPortEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
                    try {
                        int dataSize = inputStream.available();
                        while (dataSize > 0) {
                            byte[] readBuffer = new byte[dataSize];
                            inputStream.read(readBuffer);
                            LOGGER.debug("Data was read = " + StringUtil.byteArrayToHexString(readBuffer));
                            notifyOfData(readBuffer);
                            dataSize = inputStream.available();
                        }
                    } catch (IOException e) {
                        LOGGER.error(e.getMessage(), e);
                    }
                }
            } catch (NumberFormatException e) {
                LOGGER.error(e.getMessage(), e);
            }
        }
    }

    private void notifyOfData(byte[] data) {
        for (DataListener listener : listeners) {
            listener.handleData(data);
        }
    }
}
